home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / MoreFiles 1.1.1 / FileCopy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-18  |  13.3 KB  |  379 lines  |  [TEXT/KAHL]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    FileCopy: A robust, general purpose file copy routine.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support
  7. **
  8. **    File:        FileCopy.c
  9. **
  10. **    Copyright © 1992-1994 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #ifndef __FILECOPY__
  23. #include "FileCopy.h"
  24. #endif
  25.  
  26.  
  27. /*****************************************************************************/
  28.  
  29. /* local constants */
  30.  
  31. /*    The deny-mode privileges to use when opening the source and destination files. */
  32.  
  33. enum
  34. {
  35.     srcCopyMode = dmRdDenyWr,
  36.     dstCopyMode = dmWrDenyRdWr
  37. };
  38.  
  39. /*    The largest (16K) and smallest (.5K) copy buffer to use if the caller doesn't supply 
  40. **    their own copy buffer. */
  41.  
  42. enum
  43. {
  44.     bigCopyBuffSize  = 0x00004000,
  45.     minCopyBuffSize  = 0x00000200
  46. };
  47.  
  48. /*****************************************************************************/
  49.  
  50. /* static prototypes */
  51.  
  52. static    void    pcpy(StringPtr d,
  53.                      StringPtr s);
  54.  
  55. static    OSErr    PreflightFileCopySpace(short srcVRefNum,
  56.                                        long srcDirID,
  57.                                        const Str255 srcName,
  58.                                        StringPtr dstVolName,
  59.                                        short dstVRefNum,
  60.                                        Boolean *spaceOK);
  61. /*    PreflightFileCopySpace determines if there's enough space on a
  62.     volume to copy the specified file to that volume.
  63.     Note: The results of this routine are not perfect. For example if the
  64.     volume's catalog or extents overflow file grows when the new file is
  65.     created, more allocation blocks may be needed beyond those needed for
  66.     the file's data and resource forks.
  67.  
  68.     srcVRefNum        input:    Volume specification of the file's current
  69.                             location.
  70.     srcDirID        input:    Directory ID of the file's current location.
  71.     srcName            input:    The name of the file.
  72.     dstVolName        input:    A pointer to the name of the volume where
  73.                             the file will be copied or nil.
  74.     dstVRefNum        input:    Volume specification indicating the volume
  75.                             where the file will be copied.
  76.     spaceOK            output:    true if there's enough space on the volume for
  77.                             the file's data and resource forks.
  78. */
  79.  
  80. /*****************************************************************************/
  81.  
  82. /* Copy a Pascal-string. */
  83. static    void    pcpy(StringPtr d,
  84.                      StringPtr s)
  85. {
  86.     short    i;
  87.  
  88.     i = *s;
  89.     do {
  90.         d[i] = s[i];
  91.     } while (i--);
  92. }
  93.  
  94. /*****************************************************************************/
  95.  
  96. static    OSErr    PreflightFileCopySpace(short srcVRefNum,
  97.                                        long srcDirID,
  98.                                        const Str255 srcName,
  99.                                        StringPtr dstVolName,
  100.                                        short dstVRefNum,
  101.                                        Boolean *spaceOK)
  102. {
  103.     HParamBlockRec pb;
  104.     OSErr error;
  105.     Str255 tempPathname;
  106.     long dstFreeBlocks;
  107.     long dstBlksPerAllocBlk;
  108.     long srcDataBlks;
  109.     long srcResourceBlks;
  110.     
  111.     /* Get the number of 512 byte blocks per allocation block and */
  112.     /* number of free allocation blocks on the destination volume */
  113.     pb.volumeParam.ioVRefNum = dstVRefNum;
  114.     if (dstVolName == nil) {
  115.         pb.volumeParam.ioNamePtr = nil;
  116.         pb.volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  117.     }
  118.     else {
  119.         pcpy((StringPtr)tempPathname, dstVolName);    /* make a copy of the string and */
  120.         pb.volumeParam.ioNamePtr = (StringPtr)tempPathname;    /* use the copy so original isn't trashed */
  121.         pb.volumeParam.ioVolIndex = -1;    /* use ioNamePtr/ioVRefNum combination */
  122.     }
  123.     error = PBHGetVInfoSync(&pb);
  124.     if (error == noErr)
  125.     {
  126.         /* get allocation block size (always multiple of 512) and divide by 512
  127.           to get number of 512-byte blocks per allocation block */
  128.         dstBlksPerAllocBlk = (pb.volumeParam.ioVAlBlkSiz >> 9);
  129.         dstFreeBlocks = pb.volumeParam.ioVFrBlk;
  130.         
  131.         /* Now, get the size of the file's data resource forks */
  132.         pb.fileParam.ioNamePtr = (StringPtr)srcName;
  133.         pb.fileParam.ioVRefNum = srcVRefNum;
  134.         pb.fileParam.ioDirID = srcDirID;
  135.         pb.fileParam.ioFDirIndex = 0;
  136.         error = PBHGetFInfoSync(&pb);
  137.         if (error == noErr)
  138.         {
  139.             /* get number of 512-byte blocks needed for data fork */
  140.             srcDataBlks = (pb.fileParam.ioFlLgLen % 512) ? ((pb.fileParam.ioFlLgLen >> 9) + 1) : (pb.fileParam.ioFlLgLen >> 9);
  141.             /* now, calculate number of new allocation blocks needed */
  142.             srcDataBlks = (srcDataBlks % dstBlksPerAllocBlk) ? ((srcDataBlks / dstBlksPerAllocBlk) + 1) : (srcDataBlks / dstBlksPerAllocBlk);
  143.         
  144.             /* get number of 512-byte blocks needed for resource fork */
  145.             srcResourceBlks = (pb.fileParam.ioFlRLgLen % 512) ? ((pb.fileParam.ioFlRLgLen >> 9) + 1) : (pb.fileParam.ioFlRLgLen >> 9);
  146.             /* now, calculate number of new allocation blocks needed */
  147.             srcResourceBlks = (srcResourceBlks % dstBlksPerAllocBlk) ? ((srcResourceBlks / dstBlksPerAllocBlk) + 1) : (srcResourceBlks / dstBlksPerAllocBlk);
  148.             
  149.             /* Is there enough room on the destination volume for the source file? */
  150.             *spaceOK = ((srcDataBlks + srcResourceBlks) <= dstFreeBlocks);
  151.         }
  152.     }
  153.     return (error);
  154. }
  155.  
  156. /*****************************************************************************/
  157.  
  158. pascal    OSErr    FileCopy(short srcVRefNum,
  159.                          long srcDirID,
  160.                          const Str255 srcName,
  161.                          short dstVRefNum,
  162.                          long dstDirID,
  163.                          StringPtr dstPathname,
  164.                          StringPtr copyName,
  165.                          Ptr copyBufferPtr,
  166.                          long copyBufferSize,
  167.                          Boolean preflight)
  168. {
  169.     OSErr    err;
  170.  
  171.     short    srcRefNum = 0,            /* 0 when source data and resource fork are closed  */
  172.             dstDataRefNum = 0,        /* 0 when destination data fork is closed */
  173.             dstRsrcRefNum = 0;        /* 0 when destination resource fork is closed */
  174.     
  175.     Str63    dstName;                /* The filename of the destination. It might be the
  176.                                     ** source filename, it might be a new name... */
  177.     
  178.     GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */
  179.     long    srcServerAdr;            /* AppleTalk server address of source (if any) */
  180.     
  181.     Boolean    dstCreated = false,        /* true when destination file has been created */
  182.             ourCopyBuffer = false,    /* true if we had to allocate the copy buffer */
  183.             isDirectory;            /* true if destination is really a directory */
  184.     
  185.     long    tempLong;
  186.     short    tempInt;
  187.     
  188.     Boolean    spaceOK;                /* true if there's enough room to copy the file to the destination volume */
  189.  
  190.     /* Preflight for size */
  191.     if (preflight) {
  192.         err = PreflightFileCopySpace(srcVRefNum, srcDirID, srcName,
  193.                                      dstPathname, dstVRefNum, &spaceOK);
  194.         if (err != noErr)
  195.             return(err);
  196.         if (!spaceOK)
  197.             return(dskFulErr);
  198.     }
  199.  
  200.     /* get the destination's real dirID and make sure it really is a directory */
  201.     err = GetDirID(dstVRefNum, dstDirID, dstPathname, &dstDirID, &isDirectory);
  202.     if (err != noErr)
  203.         goto ErrorExit;
  204.     if (!isDirectory)
  205.         return (dirNFErr);
  206.  
  207.     /* get the destination's real vRefNum */
  208.     err = DetermineVRefNum(dstPathname, dstVRefNum, &dstVRefNum);
  209.     if (err != noErr)
  210.         goto ErrorExit;
  211.     
  212.     /* See if PBHCopyFile can be used.  Using PBHCopyFile saves time by letting the file server
  213.     ** copy the file if the source and destination locations are on the same file server. */
  214.     tempLong = sizeof(infoBuffer);
  215.     err = HGetVolParms((StringPtr)srcName, srcVRefNum, &infoBuffer, &tempLong);
  216.     if ((err != noErr) && (err != paramErr))
  217.         return(err);
  218.  
  219.     if ((err != paramErr) && hasCopyFile(infoBuffer)) {
  220.         /* The source volume supports PBHCopyFile. */
  221.         srcServerAdr = infoBuffer.vMServerAdr;
  222.  
  223.         /* Now, see if the destination volume is on the same file server. */
  224.         tempLong = sizeof(infoBuffer);
  225.         err = HGetVolParms(nil, dstVRefNum, &infoBuffer, &tempLong);
  226.         if ((err != noErr) && (err != paramErr))
  227.             return(err);
  228.         if ((err != paramErr) && (srcServerAdr == infoBuffer.vMServerAdr)) {
  229.             /* Source and Dest are on same server and PBHCopyFile is supported. Copy with CopyFile. */
  230.             err = HCopyFile(srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, nil, copyName);
  231.             if (err != noErr)
  232.                 goto ErrorExit;
  233.             /* AppleShare's CopyFile clears the isAlias bit, so I still need to attempt to copy
  234.                the File's attributes to attempt to get things right. */
  235.             return(CopyFileMgrAttributes(srcVRefNum, srcDirID, (StringPtr)srcName,
  236.                                          dstVRefNum, dstDirID, (StringPtr)©Name, true));
  237.         }
  238.     }
  239.  
  240.     /* If we're here, then PBHCopyFile couldn't be used so we have to copy the file by hand. */
  241.  
  242.     /* Make sure a copy buffer is allocated. */
  243.     if (copyBufferPtr == nil) {
  244.         /* The caller didn't supply a copy buffer so grab one from the application heap.
  245.         ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
  246.         ** If 512 bytes aren't available, we're in trouble. */
  247.         copyBufferSize = bigCopyBuffSize;
  248.         copyBufferPtr = NewPtr(copyBufferSize);
  249.         if (copyBufferPtr == nil) {
  250.             copyBufferSize = minCopyBuffSize;
  251.             copyBufferPtr = NewPtr(copyBufferSize);
  252.             if (copyBufferPtr == nil)
  253.                 return(MemError());
  254.         }
  255.         ourCopyBuffer = true;
  256.     }
  257.  
  258.     /* Open the source data fork. */
  259.     err = HOpenAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum);
  260.     if (err != noErr)
  261.         goto ErrorExit;
  262.     
  263.     /* See if the copy will be renamed. */
  264.     if (copyName != nil)                /* Did caller supply copy file name? */
  265.         pcpy((StringPtr)&dstName, copyName);    /* Yes, use the caller supplied copy file name. */
  266.     else {                                /* They didn't, so get the source file name and use it. */
  267.         err = GetFileLocation(srcRefNum, &tempInt, &tempLong, (StringPtr)&dstName);
  268.         if (err != noErr)
  269.             goto ErrorExit;
  270.     }
  271.  
  272.     /* Create the destination file. */
  273.     err = HCreateMinimum(dstVRefNum, dstDirID, dstName);
  274.     if (err != noErr)
  275.         goto ErrorExit;
  276.     dstCreated = true;    /* After creating the destination file, any
  277.                         ** error conditions should delete the destination file */
  278.  
  279.     /* An AppleShare dropbox folder is a folder for which the user has the Make Changes
  280.     ** privilege (write access), but not See Files (read access) and See Folders (search access).
  281.     ** Copying a file into an AppleShare dropbox presents some special problems. Here are the
  282.     ** rules we have to follow to copy a file into a dropbox:
  283.     ** • File attributes can be changed only when both forks of a file are empty.
  284.     ** • DeskTop Manager comments can be added to a file only when both forks of a file 
  285.     **   are empty.
  286.     ** • A fork can be opened for write access only when both forks of a file are empty.
  287.     ** So, with those rules to live with, we'll do those operations now while both forks
  288.     ** are empty. */
  289.  
  290.     /* Copy attributes but don't lock the destination. */
  291.     err = CopyFileMgrAttributes(srcVRefNum, srcDirID, (StringPtr)srcName,
  292.                                 dstVRefNum, dstDirID, (StringPtr)&dstName, false);
  293.     if (err != noErr)
  294.         goto ErrorExit;
  295.  
  296.     /* Attempt to copy the comments while both forks are empty.
  297.     ** Ignore the result because we really don't care if it worked or not. */
  298.     CopyComment(srcVRefNum, srcDirID, (StringPtr)srcName, dstVRefNum, dstDirID, (StringPtr)&dstName);
  299.  
  300.     /* Open the destination data fork. */
  301.     err = HOpenAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstDataRefNum);
  302.     if (err != noErr)
  303.         goto ErrorExit;
  304.  
  305.     /* Open the destination resource fork. */
  306.     err = HOpenRFAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstRsrcRefNum);
  307.     if (err != noErr)
  308.         goto ErrorExit;
  309.  
  310.     /* Copy the data fork. */
  311.     err = CopyFork(srcRefNum, dstDataRefNum, copyBufferPtr, copyBufferSize);
  312.     if (err != noErr)
  313.         goto ErrorExit;
  314.  
  315.     /* Close both data forks and clear reference numbers. */
  316.     FSClose(srcRefNum);
  317.     FSClose(dstDataRefNum);
  318.     srcRefNum = dstDataRefNum = 0;
  319.  
  320.     /* Open the source resource fork. */
  321.     err = HOpenRFAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum);
  322.     if (err != noErr)
  323.         goto ErrorExit;
  324.  
  325.     /* Copy the resource fork. */
  326.     err = CopyFork(srcRefNum, dstRsrcRefNum, copyBufferPtr, copyBufferSize);
  327.     if (err != noErr)
  328.         goto ErrorExit;
  329.  
  330.     /* Close both resource forks and clear reference numbers. */
  331.     FSClose(srcRefNum);
  332.     FSClose(dstRsrcRefNum);
  333.     srcRefNum = dstRsrcRefNum = 0;
  334.  
  335.     /* Get rid of the copy buffer if we allocated it. */
  336.     if (ourCopyBuffer)
  337.         DisposPtr(copyBufferPtr);
  338.  
  339.     /* Attempt to copy attributes again to set mod date.  Copy lock condition this time
  340.     ** since we're done with the copy operation.  This operation will fail if we're copying
  341.     ** into an AppleShare dropbox, so we don't check for error conditions. */
  342.     CopyFileMgrAttributes(srcVRefNum, srcDirID, (StringPtr)srcName,
  343.                             dstVRefNum, dstDirID, (StringPtr)&dstName, true);
  344.  
  345.     /* Hey, we did it! */
  346.     return (noErr);
  347.     
  348. ErrorExit:
  349.     if (srcRefNum)
  350.         FSClose(srcRefNum);        /* Close the source file */
  351.     if (dstDataRefNum)
  352.         FSClose(dstDataRefNum);    /* Close the destination file data fork */
  353.     if (dstRsrcRefNum)
  354.         FSClose(dstRsrcRefNum);    /* Close the destination file resource fork */
  355.     if (dstCreated)
  356.         HDelete(dstVRefNum, dstDirID, dstName);    /* Delete dest file.  This may fail if the file 
  357.                                                    is in a "drop folder" */
  358.     if (ourCopyBuffer)            /* dispose of any memory we allocated */
  359.         DisposPtr(copyBufferPtr);
  360.     return (err);
  361. }
  362.  
  363. /*****************************************************************************/
  364.  
  365. pascal    OSErr    FSpFileCopy(const FSSpec *srcSpec,
  366.                             const FSSpec *dstSpec,
  367.                             StringPtr copyName,
  368.                             Ptr copyBufferPtr,
  369.                             long copyBufferSize,
  370.                             Boolean preflight)
  371. {
  372.     return (FileCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
  373.                      dstSpec->vRefNum, dstSpec->parID, (StringPtr)dstSpec->name,
  374.                      copyName, copyBufferPtr, copyBufferSize, preflight));
  375. }
  376.  
  377. /*****************************************************************************/
  378.  
  379.